package ch.ti.bfh.app.loodel.web.security;

import ch.ti.bfh.app.loodel.service.MemberData;
import ch.ti.bfh.app.loodel.service.MemberRegistrationService;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.openid.OpenIDAttribute;
import org.springframework.security.openid.OpenIDAuthenticationToken;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.util.List;

@Transactional
public class OpenIDAuthenticationUserDetailsService implements AuthenticationUserDetailsService<OpenIDAuthenticationToken> {

    private final MemberRegistrationService memberRegistrationService;

    private final UserDetailsService userDetailsService;

    @Inject
    public OpenIDAuthenticationUserDetailsService(MemberRegistrationService memberRegistrationService, UserDetailsService userDetailsService) {
        this.memberRegistrationService = memberRegistrationService;
        this.userDetailsService = userDetailsService;
    }

    /**
     * Implementation of {@code AuthenticationUserDetailsService} which allows full access to the submitted
     * {@code Authentication} object. Used by the OpenIDAuthenticationProvider.
     */
    public UserDetails loadUserDetails(OpenIDAuthenticationToken token) {
        String userMail = getEMailFromAttributes(token.getAttributes());
        MemberData memberByEMail = memberRegistrationService.findMemberByEMail(userMail);
        if (memberByEMail != null) {
            return userDetailsService.loadUserByUsername(userMail);
        } else {
            MemberData newMemberData = buildNewMemberFromOpenIDAutenticationToken(token);
            memberRegistrationService.registerNewMember(newMemberData.geteMail(), newMemberData.getFullName());
            return userDetailsService.loadUserByUsername(newMemberData.geteMail());
        }
    }

    private MemberData buildNewMemberFromOpenIDAutenticationToken(OpenIDAuthenticationToken token) {
        String email = null;
        String firstName = null;
        String lastName = null;
        String fullName = null;

        List<OpenIDAttribute> attributes = token.getAttributes();

        for (OpenIDAttribute attribute : attributes) {
            if (attribute.getName().equals("email")) {
                email = attribute.getValues().get(0);
            }

            if (attribute.getName().equals("firstname")) {
                firstName = attribute.getValues().get(0);
            }

            if (attribute.getName().equals("lastname")) {
                lastName = attribute.getValues().get(0);
            }

            if (attribute.getName().equals("fullname")) {
                fullName = attribute.getValues().get(0);
            }
        }
        if (fullName == null) {
            StringBuilder fullNameBldr = new StringBuilder();

            if (firstName != null) {
                fullNameBldr.append(firstName);
            }

            if (lastName != null) {
                fullNameBldr.append(" ").append(lastName);
            }
            fullName = fullNameBldr.toString();
        }
        return new MemberData(email, fullName);
    }

    private String getEMailFromAttributes(List<OpenIDAttribute> openIDAttributes) {
        for (OpenIDAttribute openIDAttribute : openIDAttributes) {
            if (openIDAttribute.getName().equalsIgnoreCase("email")) {
                return openIDAttribute.getValues().get(0);
            }
        }
        throw new InternalAuthenticationServiceException("Loodel needs your email address in order to identify you");
    }
}